#ifndef _ELF_DWARF_LINE_H_
#define _ELF_DWARF_LINE_H_

#include "loader/elf.h"
#include "elf_dwarf_util.h"
#include "std_errno.h"

#define STD_OP_CODE_MAX	256
typedef struct {
	char		*filename;
	uint32		dir;
	uint32		time;
	uint32		size;
} ElfDwarfLineEntryHeaderFileType;

typedef struct {
	uint32 					total_length;
	uint16 					version;
	uint32 					prologue_length;
	uint8  					minimum_instruction_length;
	uint8					maximum_operations_per_instruction;
	uint8  					default_is_stmt;
	sint8  					line_base;
	uint8  					line_range;
	uint8  					opcode_base;
	uint8  					standard_opcode_lengths[STD_OP_CODE_MAX];
	ElfPointerArrayType		*include_directories;
	ElfPointerArrayType		*file_names;
} ElfDwarfLineEntryHeaderType;


extern Std_ReturnType elf_dwarf_line_load(uint8 *elf_data);

typedef enum {
	OPTYPE_EXTENDED	= 0,
	OPTYPE_STANDARD,
	OPTYPE_SPECIAL,
} ElfDwarfLineOpType;

typedef struct {
	uint32 advance_addr;
	sint32 advance_line;
} DwLineSpecialType;


typedef enum {
	DW_LNE_end_sequence = 1,
	DW_LNE_set_address,
	DW_LINE_define_file,
	DW_LNE_set_discriminator, /* dwarf ver.4 */
	DW_LNE_lo_user = 0x80,
	DW_LNE_hi_user = 0xff,
	DW_LINE_unknown,
} ElfDwarfLineOpExtendedType;

typedef struct {
	uint32 addr;
} DwLineSetAddressType;
typedef struct {
	char 	*file;
	uint32	dir;
	uint32	time;
	uint32	size;
} DwLineDefineFileType;
typedef struct {
	uint32	discriminator;
} DwLineSetDiscriminatorType;
typedef enum {
	DW_LNS_copy = 1,
	DW_LNS_advance_pc,
	DW_LNS_advance_line,
	DW_LNS_set_file,
	DW_LNS_set_column,
	DW_LNS_negate_stmt,
	DW_LNS_set_basic_block,
	DW_LNS_const_add_pc,
	DW_LNS_fixed_advance_pc,
} ElfDwarfLineOpStandardType;

typedef struct {
	uint32 advance_size;
} DwLnsAdvancePcType;

typedef struct {
	sint32 advance_line;
} DwLnsAdvanceLineType;

typedef struct {
	uint32 file;
} DwLnsSetFileType;

typedef struct {
	uint32 column;
} DwLnsSetColumnType;

typedef struct {
	uint32 fixed_advance_pc;
} DwLnsFixedAdvancePcType;
typedef struct {
	uint32 const_add_pc;
} DwLnsConsdAddPcType;

typedef struct {
	ElfDwarfLineEntryHeaderType		*hdr;
	uint32 							size;
	ElfDwarfLineOpType				type;
	int								subtype;
	union {
		DwLineSetAddressType		extSetAddress;
		DwLineDefineFileType		extDefineFile;
		DwLineSetDiscriminatorType	extSetDescriminator;
		DwLnsAdvancePcType			stdAdvancePc;
		DwLnsAdvanceLineType		stdAdvanceLine;
		DwLnsSetFileType			stdSetFile;
		DwLnsSetColumnType			stdSetColumn;
		DwLnsFixedAdvancePcType		stdFixedAdvancePc;
		DwLnsConsdAddPcType			stdConstAddPc;
		DwLineSpecialType			special;
	} args;
} ElfDwarfLineParsedOpCodeType;


typedef struct {
	ElfDwarfLineEntryHeaderType		*header;
	ElfPointerArrayType				*ops;
} ElfDwarfLineEntryType;


typedef struct {
	ElfPointerArrayType				*entries;
} ElfDwarfLineType;

extern ElfDwarfLineType *elf_dwarf_line_alloc_empty_ElfDwarfLine(void);
extern ElfDwarfLineEntryHeaderType *elf_dwarf_line_alloc_empty_ElfDwarfLineEntryHeader(void);
extern ElfDwarfLineEntryType *elf_dwarf_line_alloc_empty_ElfDwarfLineEntry(void);
extern ElfDwarfLineParsedOpCodeType *elf_dwarf_line_alloc_empty_ElfDwarfLineParsedOpCode(void);

typedef struct {
	/*
	 * The program-counter value corresponding to a machine instruction
	 * generated by the compiler.
	 */
	uint32	address;

	/*
	 * An unsigned integer representing the index of an operation within a VLIW
	 * instruction. The index of the first operation is 0. For non-VLIW
	 * architectures, this register will always be 0.
	 * The address and op_index registers, taken together, form an operation
	 * pointer that can reference any individual operation with the instruction
	 * stream.
	 */
	uint32 op_index;
	/*
	 * An unsigned integer indicating the identity of the source file
	 * corresponding to a machine instruction.
	 */
	uint32	file;
	/*
	 * An unsigned integer indicating a source line number. Lines are
	 * numbered beginning at 1. The compiler may emit the value 0 in cases
	 * where an instruction cannot be attributed to any source line.
	 */
	uint32	line;
	/*
	 * An unsigned integer indicating a column number within a source line.
	 * Columns are numbered beginning at 1. The value 0 is reserved to
	 * indicate that a statement begins at the ‘‘left edge’’ of the line.
	 */
	uint32	column;
	/*
	 * A boolean indicating that the current instruction is the beginning of a statement.
	 */
	bool	is_stmt;
	/*
	 * A boolean indicating that the current instruction is the beginning of a
	 * basic block.
	 */
	bool	basic_block;
	/*
	 * A boolean indicating that the current address is that of the first byte after
	 * the end of a sequence of target machine instructions.
	 */
	bool	end_sequence;
	/*
	 * A boolean indicating that the current address is one (of possibly many)
	 * where execution should be suspended for an entry breakpoint of a
	 * function.
	 */
	bool prologue_end;
	/*
	 * A boolean indicating that the current address is one (of possibly many)
	 * where execution should be suspended for an exit breakpoint of a function.
	 */
	bool epilogue_begin;
	/*
	 * An unsigned integer whose value encodes the applicable instruction set
	 * architecture for the current instruction.
	 * The encoding of instruction sets should be shared by all users of a given
	 * architecture. It is recommended that this encoding be defined by the ABI
	 * authoring committee for each architecture.
	 */
	uint32 isa;
	/*
	 * An unsigned integer identifying the block to which the current instruction
	 * belongs. Discriminator values are assigned arbitrarily by the DWARF
	 * producer and serve to distinguish among multiple blocks that may all be
	 * associated with the same source file, line, and column. Where only one
	 * block exists for a given source position, the discriminator value should be
	 * zero.
	 */
	uint32 discriminator;
} ElfDwarfLineStateMachineRegisterType;
extern void elf_dwarf_line_init_ElfDwarfLineStateMachineRegister(ElfDwarfLineStateMachineRegisterType *machine, ElfDwarfLineEntryHeaderType *hdr);
extern ElfDwarfLineType *elf_dwarf_line_get_ElfDwarfLine(void);

static inline void elf_dwarf_line_machine_init(ElfDwarfLineStateMachineRegisterType *machine)
{
	machine->address = 0;
	machine->op_index = 0;
	machine->file = 1;
	machine->line = 1;
	machine->column = 0;
	machine->basic_block = FALSE;
	machine->end_sequence = FALSE;
	machine->prologue_end = FALSE;
	machine->epilogue_begin = FALSE;
	machine->isa = 0;
	machine->discriminator = 0;
}

#endif /* _ELF_DWARF_LINE_H_ */
